import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext, ttk, colorchooser, font
import os
from datetime import datetime, timedelta
from collections import deque
import platform
import re
import json
import shutil
import base64
import hashlib
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

class EnhancedNotepad:
    def __init__(self, root):
        self.root = root
        self.root.title("增强记事本 - 未命名")
        self.root.geometry("1200x700")

        # 基本属性
        self.current_file = None
        self.is_modified = False
        self.recent_files = []
        self.max_recent = 5

        # 增强功能
        self.clipboard_history = deque(maxlen=10)
        self.default_font = self.get_default_font()
        self.current_color = "#000000"
        self.find_dialog = None

        # 文件树
        self.project_dir = None
        self.tree_data = {}
        self.dragged_item = None  # 拖拽功能

        # 排序设置
        self.sort_method = tk.StringVar(value="custom")
        self.sort_order = tk.StringVar(value="asc")

        # 安全设置
        self.encryption_key = None
        self.encryption_salt = None
        self.encryption_enabled = tk.BooleanVar(value=False)
        
        # 备份设置
        self.backup_enabled = tk.BooleanVar(value=False)
        self.backup_frequency = tk.StringVar(value="daily")
        self.backup_path = tk.StringVar(value="")
        self.last_backup_date = None

        # 回收站
        self.trash_dir = os.path.join(os.path.expanduser("~"), ".notepad_trash")
        os.makedirs(self.trash_dir, exist_ok=True)
        self.trash_retention_days = 30

        # 格式变量
        self.bold_var = tk.BooleanVar()
        self.italic_var = tk.BooleanVar()
        self.underline_var = tk.BooleanVar()

        # 初始化
        self.setup_ui()
        self.setup_bindings()
        self.load_settings()

    def get_default_font(self):
        system = platform.system()
        if system == "Windows":
            return ("Microsoft YaHei UI", 12)
        elif system == "Darwin":  # macOS
            return ("PingFang SC", 13)
        else:  # Linux
            return ("Noto Sans CJK SC", 12)

    def setup_ui(self):
        main_frame = tk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # ========== 左侧：文件树 ==========
        left_panel = tk.Frame(main_frame, width=250, bg="#f0f0f0")
        left_panel.pack(side=tk.LEFT, fill=tk.Y, padx=2, pady=2)
        left_panel.pack_propagate(False)

        # 排序工具栏
        sort_frame = tk.Frame(left_panel, bg="#f0f0f0")
        sort_frame.pack(fill=tk.X, pady=(5, 0))
        
        ttk.Label(sort_frame, text="排序:", background="#f0f0f0").pack(side=tk.LEFT, padx=(5, 2))
        sort_combo = ttk.Combobox(sort_frame, textvariable=self.sort_method, width=10, state="readonly")
        sort_combo['values'] = ('custom', 'name', 'created', 'modified')
        sort_combo.pack(side=tk.LEFT, padx=2)
        sort_combo.bind('<<ComboboxSelected>>', self.sort_tree)
        
        order_combo = ttk.Combobox(sort_frame, textvariable=self.sort_order, width=8, state="readonly")
        order_combo['values'] = ('asc', 'desc')
        order_combo.pack(side=tk.LEFT, padx=2)
        order_combo.bind('<<ComboboxSelected>>', self.sort_tree)

        tk.Label(left_panel, text="项目文件", bg="#f0f0f0", font=("SimHei", 10, "bold")).pack(pady=5)

        tree_frame = tk.Frame(left_panel)
        tree_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        self.tree = ttk.Treeview(tree_frame, show="tree", selectmode="extended")  # 改为多选模式
        self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        v_scroll = ttk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview)
        v_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.tree.configure(yscrollcommand=v_scroll.set)

        # 右键菜单
        self.tree_menu = tk.Menu(self.root, tearoff=0)
        self.tree_menu.add_command(label="新建文件", command=self.create_new_file)
        self.tree_menu.add_command(label="新建文件夹", command=self.create_new_folder)
        self.tree_menu.add_command(label="重命名", command=self.rename_item)
        self.tree_menu.add_command(label="删除", command=self.delete_item)
        self.tree_menu.add_separator()
        self.tree_menu.add_command(label="移动到回收站", command=lambda: self.delete_item(to_trash=True))
        self.tree_menu.add_command(label="批量操作", command=self.batch_operations)

        self.tree.bind("<Button-3>", self.show_tree_menu)
        self.tree.bind("<Double-1>", self.on_tree_double_click)
        
        # 拖拽绑定
        self.tree.bind("<ButtonPress-1>", self.on_tree_press)
        self.tree.bind("<B1-Motion>", self.on_tree_drag)
        self.tree.bind("<ButtonRelease-1>", self.on_tree_drop)

        btn_frame = tk.Frame(left_panel)
        btn_frame.pack(fill=tk.X, pady=5)
        ttk.Button(btn_frame, text="打开项目", command=self.open_project).pack(fill=tk.X)
        ttk.Button(btn_frame, text="保存项目", command=self.save_project_state).pack(fill=tk.X, pady=2)
        ttk.Button(btn_frame, text="回收站", command=self.show_trash).pack(fill=tk.X, pady=2)

        # ========== 右侧：主界面 ==========
        right_frame = tk.Frame(main_frame)
        right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=2, pady=2)

        # 工具栏
        self.create_toolbar(right_frame)

        # 菜单栏
        self.setup_menu()

        # 文本区域
        self.text_area = scrolledtext.ScrolledText(
            right_frame,
            wrap=tk.WORD,
            font=self.default_font,
            undo=True,
            fg=self.current_color
        )
        self.text_area.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 状态栏
        self.status_bar = tk.Label(self.root, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)

        # 绑定事件
        self.text_area.bind("<<Modified>>", self.on_text_modified)
        self.text_area.bind("<ButtonRelease-1>", self.update_format_toolbar)
        self.text_area.bind("<KeyRelease>", self.update_format_toolbar)
        self.text_area.bind("<ButtonRelease-3>", self.update_format_toolbar)

    def create_toolbar(self, parent):
        toolbar = ttk.Frame(parent, padding="5")
        toolbar.pack(side=tk.TOP, fill=tk.X)

        ttk.Button(toolbar, text="撤销", command=self.undo).pack(side=tk.LEFT, padx=2)
        ttk.Button(toolbar, text="重做", command=self.redo).pack(side=tk.LEFT, padx=2)
        ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)

        ttk.Button(toolbar, text="剪切", command=self.cut).pack(side=tk.LEFT, padx=2)
        ttk.Button(toolbar, text="复制", command=self.copy).pack(side=tk.LEFT, padx=2)
        ttk.Button(toolbar, text="粘贴", command=self.paste).pack(side=tk.LEFT, padx=2)
        ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)

        ttk.Checkbutton(toolbar, text="粗体", command=self.toggle_bold, variable=self.bold_var).pack(side=tk.LEFT, padx=2)
        ttk.Checkbutton(toolbar, text="斜体", command=self.toggle_italic, variable=self.italic_var).pack(side=tk.LEFT, padx=2)
        ttk.Checkbutton(toolbar, text="下划线", command=self.toggle_underline, variable=self.underline_var).pack(side=tk.LEFT, padx=2)

        ttk.Button(toolbar, text="颜色", command=self.choose_text_color).pack(side=tk.LEFT, padx=2)
        ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)

        ttk.Button(toolbar, text="查找/替换", command=self.show_find_replace).pack(side=tk.LEFT, padx=2)
        
        # 加密按钮
        ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)
        ttk.Checkbutton(toolbar, text="加密", command=self.toggle_encryption, variable=self.encryption_enabled).pack(side=tk.LEFT, padx=2)

    def setup_menu(self):
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)

        # 文件菜单
        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="文件", menu=file_menu)
        file_menu.add_command(label="新建", command=self.new_file, accelerator="Ctrl+N")
        file_menu.add_command(label="打开", command=self.open_file, accelerator="Ctrl+O")
        file_menu.add_separator()
        file_menu.add_command(label="保存", command=self.save_file, accelerator="Ctrl+S")
        file_menu.add_command(label="另存为", command=self.save_as_file, accelerator="Ctrl+Shift+S")
        file_menu.add_separator()
        
        # 备份子菜单
        backup_menu = tk.Menu(file_menu, tearoff=0)
        file_menu.add_cascade(label="备份设置", menu=backup_menu)
        backup_menu.add_checkbutton(label="启用自动备份", variable=self.backup_enabled, command=self.toggle_backup)
        backup_menu.add_separator()
        backup_menu.add_command(label="设置备份路径", command=self.set_backup_path)
        backup_menu.add_command(label="立即备份", command=self.perform_backup)
        
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.exit_app, accelerator="Ctrl+Q")

        # 编辑菜单
        edit_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="编辑", menu=edit_menu)
        edit_menu.add_command(label="撤销", command=self.undo, accelerator="Ctrl+Z")
        edit_menu.add_command(label="重做", command=self.redo, accelerator="Ctrl+Y")
        edit_menu.add_separator()
        edit_menu.add_command(label="剪切", command=self.cut, accelerator="Ctrl+X")
        edit_menu.add_command(label="复制", command=self.copy, accelerator="Ctrl+C")
        edit_menu.add_command(label="粘贴", command=self.paste, accelerator="Ctrl+V")
        edit_menu.add_separator()
        edit_menu.add_command(label="全选", command=self.select_all, accelerator="Ctrl+A")
        edit_menu.add_command(label="查找/替换", command=self.show_find_replace, accelerator="Ctrl+F")

        # 格式菜单
        format_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="格式", menu=format_menu)

        self.font_menu = tk.Menu(format_menu, tearoff=0)
        format_menu.add_cascade(label="字体", menu=self.font_menu)
        self.populate_font_menu()

        self.size_menu = tk.Menu(format_menu, tearoff=0)
        format_menu.add_cascade(label="字号", menu=self.size_menu)
        for size in [8, 10, 12, 14, 16, 18, 20, 24, 28, 32]:
            self.size_menu.add_command(label=str(size), command=lambda s=size: self.change_font_size(s))

        format_menu.add_separator()
        format_menu.add_command(label="粗体", command=self.toggle_bold, accelerator="Ctrl+B")
        format_menu.add_command(label="斜体", command=self.toggle_italic, accelerator="Ctrl+I")
        format_menu.add_command(label="下划线", command=self.toggle_underline, accelerator="Ctrl+U")
        format_menu.add_command(label="删除线", command=self.toggle_strikethrough)
        format_menu.add_separator()
        format_menu.add_command(label="文字颜色", command=self.choose_text_color)
        
        # 工具菜单
        tools_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="工具", menu=tools_menu)
        tools_menu.add_command(label="批量操作", command=self.batch_operations)
        tools_menu.add_command(label="清理回收站", command=self.clean_trash)
        tools_menu.add_separator()
        tools_menu.add_command(label="安全设置", command=self.security_settings)

    def populate_font_menu(self):
        try:
            all_fonts = font.families()
            fonts = [f for f in all_fonts if not f.startswith("@")]
            fonts = sorted(set(fonts))[:100]
        except:
            fonts = ["SimHei", "Microsoft YaHei", "SimSun", "KaiTi", "FangSong",
                     "Arial", "Times New Roman", "Courier New", "Consolas"]

        for f in fonts:
            self.font_menu.add_command(
                label=f,
                command=lambda font=f: self.change_font(font)
            )

    def setup_bindings(self):
        if hasattr(self, '_bindings_set'):
            return
        self._bindings_set = True

        bindings = [
            ("<Control-n>", self.new_file),
            ("<Control-o>", self.open_file),
            ("<Control-s>", self.save_file),
            ("<Control-Shift-S>", self.save_as_file),
            ("<Control-q>", self.exit_app),
            ("<Control-z>", self.undo),
            ("<Control-y>", self.redo),
            ("<Control-x>", self.cut),
            ("<Control-c>", self.copy),
            ("<Control-v>", self.paste),
            ("<Control-a>", self.select_all),
            ("<Control-f>", self.show_find_replace),
            ("<Control-b>", self.toggle_bold),
            ("<Control-i>", self.toggle_italic),
            ("<Control-u>", self.toggle_underline),
        ]
        for key, cmd in bindings:
            self.root.bind(key, lambda e, c=cmd: c())

    # ========== 新增功能实现 ==========
    
    # 1. 文件拖拽功能
    def on_tree_press(self, event):
        item = self.tree.identify_row(event.y)
        if item:
            self.dragged_item = item
            self.tree.selection_set(item)

    def on_tree_drag(self, event):
        if self.dragged_item:
            # 显示拖拽视觉效果
            pass

    def on_tree_drop(self, event):
        if not self.dragged_item:
            return
            
        target_item = self.tree.identify_row(event.y)
        if target_item and self.dragged_item != target_item:
            # 获取目标位置
            target_data = self.tree_data[target_item]
            target_path = target_data["path"]
            
            # 获取拖拽项信息
            dragged_data = self.tree_data[self.dragged_item]
            dragged_path = dragged_data["path"]
            dragged_name = os.path.basename(dragged_path)
            
            # 确定新路径
            if target_data["type"] == "folder":
                new_path = os.path.join(target_path, dragged_name)
            else:
                parent_path = os.path.dirname(target_path)
                new_path = os.path.join(parent_path, dragged_name)
            
            # 移动文件/文件夹
            try:
                shutil.move(dragged_path, new_path)
                # 更新树和数据
                self.tree.delete(self.dragged_item)
                del self.tree_data[self.dragged_item]
                
                # 重新加载受影响的部分
                if target_data["type"] == "folder":
                    parent_id = target_item
                else:
                    parent_id = target_data["parent"]
                
                if dragged_data["type"] == "folder":
                    self.load_directory_tree(new_path, parent_id)
                else:
                    item_id = self.tree.insert(parent_id, "end", text=dragged_name)
                    self.tree_data[item_id] = {
                        "path": new_path, 
                        "type": "file", 
                        "parent": parent_id
                    }
                
                self.update_status(f"已移动: {dragged_name}")
                
            except Exception as e:
                messagebox.showerror("错误", f"移动失败: {str(e)}")
        
        self.dragged_item = None

    # 2. 排序功能
    def sort_tree(self, event=None):
        if not self.project_dir:
            return
            
        method = self.sort_method.get()
        order = self.sort_order.get()
        
        # 清空当前树
        for item in self.tree.get_children():
            self.tree.delete(item)
        self.tree_data = {}
        
        # 重新加载并按指定方式排序
        self.load_directory_tree(self.project_dir, "", method, order)
        self.update_status(f"已按{method}排序")

    def load_directory_tree(self, path, parent_id, sort_method="custom", sort_order="asc"):
        name = os.path.basename(path)
        item_id = self.tree.insert(parent_id, "end", text=name, open=True)
        self.tree_data[item_id] = {"path": path, "type": "folder", "parent": parent_id}

        if os.path.isdir(path):
            try:
                entries = []
                for entry in os.listdir(path):
                    entry_path = os.path.join(path, entry)
                    entries.append((entry, entry_path))
                
                # 排序逻辑
                if sort_method == "name":
                    entries.sort(key=lambda x: x[0].lower(), reverse=(sort_order == "desc"))
                elif sort_method == "created":
                    entries.sort(key=lambda x: os.path.getctime(x[1]), reverse=(sort_order == "desc"))
                elif sort_method == "modified":
                    entries.sort(key=lambda x: os.path.getmtime(x[1]), reverse=(sort_order == "desc"))
                # custom 保持原顺序
                
                for entry, entry_path in entries:
                    if os.path.isdir(entry_path):
                        self.load_directory_tree(entry_path, item_id, sort_method, sort_order)
                    elif entry.endswith(".txt"):
                        file_id = self.tree.insert(item_id, "end", text=entry)
                        self.tree_data[file_id] = {"path": entry_path, "type": "file", "parent": item_id}
            except PermissionError:
                pass

    # 3. 批量操作功能
    def batch_operations(self):
        selected_items = self.tree.selection()
        if not selected_items:
            messagebox.showinfo("提示", "请先选择要操作的文件")
            return
            
        # 创建批量操作对话框
        dialog = tk.Toplevel(self.root)
        dialog.title("批量操作")
        dialog.geometry("300x200")
        dialog.transient(self.root)
        dialog.resizable(False, False)
        
        frame = ttk.Frame(dialog, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)
        
        ttk.Label(frame, text=f"已选择 {len(selected_items)} 个项目").pack(pady=5)
        
        # 操作按钮
        ttk.Button(frame, text="移动到文件夹", 
                  command=lambda: self.batch_move(selected_items, dialog)).pack(fill=tk.X, pady=2)
        ttk.Button(frame, text="删除到回收站", 
                  command=lambda: self.batch_delete(selected_items, dialog)).pack(fill=tk.X, pady=2)
        ttk.Button(frame, text="取消", command=dialog.destroy).pack(fill=tk.X, pady=10)

    def batch_move(self, items, dialog):
        target_dir = filedialog.askdirectory(title="选择目标文件夹")
        if not target_dir:
            return
            
        for item in items:
            data = self.tree_data.get(item)
            if data and data["type"] == "file":
                try:
                    shutil.move(data["path"], os.path.join(target_dir, os.path.basename(data["path"])))
                    self.tree.delete(item)
                    del self.tree_data[item]
                except Exception as e:
                    messagebox.showerror("错误", f"移动失败: {str(e)}")
        
        dialog.destroy()
        self.update_status(f"已移动 {len(items)} 个文件")

    def batch_delete(self, items, dialog):
        if messagebox.askyesno("确认", f"将 {len(items)} 个项目移动到回收站？"):
            for item in items:
                data = self.tree_data.get(item)
                if data:
                    try:
                        # 移动到回收站
                        trash_path = os.path.join(self.trash_dir, os.path.basename(data["path"]))
                        # 避免文件名冲突
                        counter = 1
                        while os.path.exists(trash_path):
                            name, ext = os.path.splitext(os.path.basename(data["path"]))
                            trash_path = os.path.join(self.trash_dir, f"{name}_{counter}{ext}")
                            counter += 1
                            
                        shutil.move(data["path"], trash_path)
                        self.tree.delete(item)
                        del self.tree_data[item]
                    except Exception as e:
                        messagebox.showerror("错误", f"删除失败: {str(e)}")
        
        dialog.destroy()
        self.update_status(f"已删除 {len(items)} 个文件到回收站")

    def show_trash(self):
        trash_window = tk.Toplevel(self.root)
        trash_window.title("回收站")
        trash_window.geometry("600x400")
        
        frame = ttk.Frame(trash_window, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)
        
        ttk.Label(frame, text="回收站内容 (保留30天)").pack(pady=5)
        
        # 创建树形视图显示回收站内容
        columns = ("name", "original_path", "deleted_date")
        tree = ttk.Treeview(frame, columns=columns, show="headings")
        
        tree.heading("name", text="文件名")
        tree.heading("original_path", text="原路径")
        tree.heading("deleted_date", text="删除日期")
        
        tree.column("name", width=150)
        tree.column("original_path", width=250)
        tree.column("deleted_date", width=100)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=tree.yview)
        tree.configure(yscrollcommand=scrollbar.set)
        
        tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 填充数据
        for item in os.listdir(self.trash_dir):
            item_path = os.path.join(self.trash_dir, item)
            if os.path.isfile(item_path):
                # 从文件名中提取原始信息（实际应用中可能需要更复杂的元数据存储）
                deleted_date = datetime.fromtimestamp(os.path.getctime(item_path))
                tree.insert("", "end", values=(item, "未知", deleted_date.strftime("%Y-%m-%d")))
        
        # 操作按钮框架
        btn_frame = ttk.Frame(frame)
        btn_frame.pack(fill=tk.X, pady=5)
        
        ttk.Button(btn_frame, text="恢复选中", 
                  command=lambda: self.restore_from_trash(tree)).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="永久删除", 
                  command=lambda: self.permanent_delete(tree)).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="关闭", 
                  command=trash_window.destroy).pack(side=tk.RIGHT, padx=5)

    def restore_from_trash(self, tree):
        selected = tree.selection()
        if not selected:
            messagebox.showinfo("提示", "请先选择要恢复的文件")
            return
            
        for item in selected:
            values = tree.item(item, "values")
            filename = values[0]
            original_path = values[1]  # 实际应用中需要存储原始路径
            
            if original_path == "未知":
                # 让用户选择恢复位置
                target_dir = filedialog.askdirectory(title="选择恢复位置")
                if not target_dir:
                    continue
                target_path = os.path.join(target_dir, filename)
            else:
                target_path = original_path
                
            try:
                shutil.move(os.path.join(self.trash_dir, filename), target_path)
                tree.delete(item)
                self.update_status(f"已恢复: {filename}")
            except Exception as e:
                messagebox.showerror("错误", f"恢复失败: {str(e)}")

    def permanent_delete(self, tree):
        selected = tree.selection()
        if not selected:
            return
            
        if messagebox.askyesno("确认", "永久删除选中的文件？此操作不可恢复。"):
            for item in selected:
                values = tree.item(item, "values")
                filename = values[0]
                try:
                    os.remove(os.path.join(self.trash_dir, filename))
                    tree.delete(item)
                except Exception as e:
                    messagebox.showerror("错误", f"删除失败: {str(e)}")
            
            self.update_status(f"已永久删除 {len(selected)} 个文件")

    def clean_trash(self):
        # 清理超过30天的文件
        cutoff_date = datetime.now() - timedelta(days=self.trash_retention_days)
        count = 0
        
        for filename in os.listdir(self.trash_dir):
            filepath = os.path.join(self.trash_dir, filename)
            if os.path.isfile(filepath):
                file_date = datetime.fromtimestamp(os.path.getctime(filepath))
                if file_date < cutoff_date:
                    try:
                        os.remove(filepath)
                        count += 1
                    except:
                        pass
        
        messagebox.showinfo("完成", f"已清理 {count} 个过期文件")
        self.update_status(f"已清理回收站: {count} 个文件")

    # 4. 备份功能
    def toggle_backup(self):
        if self.backup_enabled.get():
            if not self.backup_path.get():
                self.set_backup_path()
            if self.backup_path.get():
                self.schedule_backup()
        self.save_settings()

    def set_backup_path(self):
        path = filedialog.askdirectory(title="选择备份目录")
        if path:
            self.backup_path.set(path)
            self.save_settings()
            self.update_status(f"备份路径设置为: {path}")

    def schedule_backup(self):
        # 检查是否需要备份
        if not self.backup_enabled.get() or not self.backup_path.get():
            return
            
        now = datetime.now()
        need_backup = False
        
        if self.backup_frequency.get() == "daily":
            if not self.last_backup_date or self.last_backup_date.date() < now.date():
                need_backup = True
        elif self.backup_frequency.get() == "weekly":
            if not self.last_backup_date or (now - self.last_backup_date).days >= 7:
                need_backup = True
                
        if need_backup:
            self.perform_backup()
            
        # 每天检查一次
        self.root.after(86400000, self.schedule_backup)  # 24小时

    def perform_backup(self):
        if not self.project_dir:
            messagebox.showinfo("提示", "请先打开一个项目")
            return
            
        try:
            # 创建带时间戳的备份文件夹
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            backup_dir = os.path.join(self.backup_path.get(), f"backup_{timestamp}")
            os.makedirs(backup_dir, exist_ok=True)
            
            # 复制项目文件
            for root, dirs, files in os.walk(self.project_dir):
                for file in files:
                    if file.endswith(".txt"):
                        src_path = os.path.join(root, file)
                        rel_path = os.path.relpath(src_path, self.project_dir)
                        dst_path = os.path.join(backup_dir, rel_path)
                        os.makedirs(os.path.dirname(dst_path), exist_ok=True)
                        shutil.copy2(src_path, dst_path)
            
            self.last_backup_date = datetime.now()
            self.save_settings()
            self.update_status(f"已备份到: {backup_dir}")
            
        except Exception as e:
            messagebox.showerror("错误", f"备份失败: {str(e)}")

    # 5. 加密功能 - 使用cryptography库
    def toggle_encryption(self):
        if self.encryption_enabled.get():
            if not self.encryption_key:
                self.set_encryption_key()
            if self.encryption_key:
                self.update_status("加密已启用")
            else:
                self.encryption_enabled.set(False)
        else:
            self.update_status("加密已禁用")

    def set_encryption_key(self):
        dialog = tk.Toplevel(self.root)
        dialog.title("设置加密密钥")
        dialog.geometry("300x150")
        dialog.transient(self.root)
        dialog.resizable(False, False)
        
        frame = ttk.Frame(dialog, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)
        
        ttk.Label(frame, text="输入加密密码:").pack(pady=5)
        
        password = tk.StringVar()
        entry = ttk.Entry(frame, textvariable=password, show="*")
        entry.pack(fill=tk.X, pady=5)
        entry.focus()
        
        def confirm():
            if password.get():
                # 生成salt并派生密钥
                self.encryption_salt = os.urandom(16)
                kdf = PBKDF2HMAC(
                    algorithm=hashes.SHA256(),
                    length=32,
                    salt=self.encryption_salt,
                    iterations=100000,
                )
                key_material = password.get().encode()
                key = base64.urlsafe_b64encode(kdf.derive(key_material))
                self.encryption_key = Fernet(key)
                
                # 保存salt供后续使用
                with open(os.path.join(os.path.dirname(__file__), ".encryption_salt"), "wb") as f:
                    f.write(self.encryption_salt)
                dialog.destroy()
            else:
                messagebox.showwarning("警告", "密码不能为空")
        
        ttk.Button(frame, text="确定", command=confirm).pack(pady=5)

    def encrypt_data(self, data):
        if not self.encryption_key:
            return data
            
        try:
            encrypted = self.encryption_key.encrypt(data.encode())
            return base64.b64encode(encrypted).decode('utf-8')
        except:
            return data

    def decrypt_data(self, encrypted_data):
        if not self.encryption_key:
            return encrypted_data
            
        try:
            encrypted = base64.b64decode(encrypted_data.encode('utf-8'))
            decrypted = self.encryption_key.decrypt(encrypted)
            return decrypted.decode('utf-8')
        except:
            # 解密失败，可能不是加密数据
            return encrypted_data

    def security_settings(self):
        dialog = tk.Toplevel(self.root)
        dialog.title("安全设置")
        dialog.geometry("400x300")
        dialog.transient(self.root)
        dialog.resizable(False, False)
        
        frame = ttk.Frame(dialog, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)
        
        ttk.Label(frame, text="安全与隐私设置", font=("", 12, "bold")).pack(pady=10)
        
        ttk.Checkbutton(frame, text="启用文件加密", variable=self.encryption_enabled, 
                       command=self.toggle_encryption).pack(anchor=tk.W, pady=5)
        
        ttk.Button(frame, text="更改加密密码", 
                  command=self.change_encryption_key).pack(anchor=tk.W, pady=5)
        
        ttk.Separator(frame).pack(fill=tk.X, pady=10)
        
        ttk.Label(frame, text="自动清理回收站:").pack(anchor=tk.W, pady=5)
        
        retention_frame = ttk.Frame(frame)
        retention_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(retention_frame, text="保留天数:").pack(side=tk.LEFT)
        days_var = tk.StringVar(value=str(self.trash_retention_days))
        spinbox = ttk.Spinbox(retention_frame, from_=1, to=365, textvariable=days_var, width=5)
        spinbox.pack(side=tk.LEFT, padx=5)
        
        def save_retention():
            try:
                self.trash_retention_days = int(days_var.get())
                self.save_settings()
                dialog.destroy()
            except ValueError:
                messagebox.showerror("错误", "请输入有效的天数")
        
        ttk.Button(frame, text="保存设置", command=save_retention).pack(pady=10)

    def change_encryption_key(self):
        # 先验证旧密码
        dialog = tk.Toplevel(self.root)
        dialog.title("更改加密密码")
        dialog.geometry("300x200")
        dialog.transient(self.root)
        dialog.resizable(False, False)
        
        frame = ttk.Frame(dialog, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)
        
        ttk.Label(frame, text="输入旧密码:").pack(pady=5)
        
        old_password = tk.StringVar()
        ttk.Entry(frame, textvariable=old_password, show="*").pack(fill=tk.X, pady=5)
        
        ttk.Label(frame, text="输入新密码:").pack(pady=5)
        
        new_password = tk.StringVar()
        ttk.Entry(frame, textvariable=new_password, show="*").pack(fill=tk.X, pady=5)
        
        ttk.Label(frame, text="确认新密码:").pack(pady=5)
        
        confirm_password = tk.StringVar()
        ttk.Entry(frame, textvariable=confirm_password, show="*").pack(fill=tk.X, pady=5)
        
        def confirm():
            if not old_password.get():
                messagebox.showwarning("警告", "请输入旧密码")
                return
                
            if new_password.get() != confirm_password.get():
                messagebox.showwarning("警告", "新密码不匹配")
                return
                
            # 验证旧密码
            try:
                with open(os.path.join(os.path.dirname(__file__), ".encryption_salt"), "rb") as f:
                    salt = f.read()
                
                kdf = PBKDF2HMAC(
                    algorithm=hashes.SHA256(),
                    length=32,
                    salt=salt,
                    iterations=100000,
                )
                key_material = old_password.get().encode()
                key = base64.urlsafe_b64encode(kdf.derive(key_material))
                test_fernet = Fernet(key)
                
                # 测试加密解密（简单的验证方式）
                test_data = "test"
                encrypted = test_fernet.encrypt(test_data.encode())
                decrypted = test_fernet.decrypt(encrypted).decode()
                
                if decrypted == test_data:
                    # 如果旧密码正确，设置新密码
                    new_salt = os.urandom(16)
                    new_kdf = PBKDF2HMAC(
                        algorithm=hashes.SHA256(),
                        length=32,
                        salt=new_salt,
                        iterations=100000,
                    )
                    new_key = base64.urlsafe_b64encode(new_kdf.derive(new_password.get().encode()))
                    self.encryption_key = Fernet(new_key)
                    self.encryption_salt = new_salt
                    
                    with open(os.path.join(os.path.dirname(__file__), ".encryption_salt"), "wb") as f:
                        f.write(new_salt)
                    dialog.destroy()
                    messagebox.showinfo("成功", "密码已更改")
                else:
                    messagebox.showerror("错误", "旧密码不正确")
            except:
                messagebox.showerror("错误", "验证旧密码时发生错误")
        
        ttk.Button(frame, text="确定", command=confirm).pack(pady=10)

    # 6. 设置保存和加载
    def save_settings(self):
        settings = {
            "backup_enabled": self.backup_enabled.get(),
            "backup_frequency": self.backup_frequency.get(),
            "backup_path": self.backup_path.get(),
            "last_backup_date": self.last_backup_date.isoformat() if self.last_backup_date else None,
            "trash_retention_days": self.trash_retention_days,
            "encryption_enabled": self.encryption_enabled.get(),
            "recent_files": self.recent_files
        }
        
        try:
            with open(os.path.join(os.path.dirname(__file__), "notepad_settings.json"), "w", encoding='utf-8') as f:
                json.dump(settings, f, indent=2, ensure_ascii=False)
        except:
            pass

    def load_settings(self):
        try:
            with open(os.path.join(os.path.dirname(__file__), "notepad_settings.json"), "r", encoding='utf-8') as f:
                settings = json.load(f)
                
            self.backup_enabled.set(settings.get("backup_enabled", False))
            self.backup_frequency.set(settings.get("backup_frequency", "daily"))
            self.backup_path.set(settings.get("backup_path", ""))
            
            if settings.get("last_backup_date"):
                self.last_backup_date = datetime.fromisoformat(settings["last_backup_date"])
                
            self.trash_retention_days = settings.get("trash_retention_days", 30)
            self.encryption_enabled.set(settings.get("encryption_enabled", False))
            self.recent_files = settings.get("recent_files", [])
            
            # 加载加密salt
            try:
                with open(os.path.join(os.path.dirname(__file__), ".encryption_salt"), "rb") as f:
                    self.encryption_salt = f.read()
                # 需要用户输入密码来重新生成密钥
            except:
                pass
                
        except FileNotFoundError:
            pass

    # 修改现有的文件操作方法以支持加密
    def open_file(self, filepath=None):
        if self.check_save():
            if not filepath:
                file_types = [("文本文件", "*.txt"), ("所有文件", "*.*")]
                filepath = filedialog.askopenfilename(filetypes=file_types)
            if filepath:
                try:
                    with open(filepath, 'r', encoding='utf-8') as file:
                        content = file.read().rstrip('\n')
                    
                    # 解密内容（如果是加密的）
                    if self.encryption_enabled.get() and self.encryption_key:
                        content = self.decrypt_data(content)
                    
                    self.text_area.delete(1.0, tk.END)
                    self.text_area.insert(1.0, content)
                    self.current_file = filepath
                    self.is_modified = False
                    self.text_area.edit_reset()
                    self.update_title()
                    self.add_to_recent(filepath)
                    self.update_status(f"已打开: {os.path.basename(filepath)}")
                    self.select_tree_item_by_path(filepath)
                except Exception as e:
                    messagebox.showerror("错误", f"无法打开文件:\n{str(e)}")

    def save_file(self):
        if self.current_file:
            try:
                content = self.text_area.get(1.0, tk.END + "-1c")
                
                # 加密内容（如果启用加密）
                if self.encryption_enabled.get() and self.encryption_key:
                    content = self.encrypt_data(content)
                
                with open(self.current_file, 'w', encoding='utf-8') as file:
                    file.write(content)
                self.is_modified = False
                self.update_title()
                self.update_status(f"已保存: {os.path.basename(self.current_file)}")
                return True
            except Exception as e:
                messagebox.showerror("错误", f"保存失败:\n{str(e)}")
                return False
        else:
            return self.save_as_file()

    # 保留所有原有的方法...
    def on_text_modified(self, event):
        if self.text_area.edit_modified():
            self.is_modified = True
            self.update_title()
        self.text_area.edit_modified(False)

    def update_title(self):
        filename = os.path.basename(self.current_file) if self.current_file else "未命名"
        modified = "*" if self.is_modified else ""
        self.root.title(f"增强记事本 - {filename}{modified}")

    def update_status(self, message):
        self.status_bar.config(text=message)

    def new_file(self):
        if self.check_save():
            self.text_area.delete(1.0, tk.END)
            self.current_file = None
            self.is_modified = False
            self.text_area.edit_reset()
            self.update_title()
            self.update_status("新建文件")

    def save_as_file(self):
        file_types = [("文本文件", "*.txt"), ("所有文件", "*.*")]
        filepath = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=file_types)
        if filepath:
            try:
                content = self.text_area.get(1.0, tk.END + "-1c")
                
                # 加密内容（如果启用加密）
                if self.encryption_enabled.get() and self.encryption_key:
                    content = self.encrypt_data(content)
                
                with open(filepath, 'w', encoding='utf-8') as file:
                    file.write(content)
                self.current_file = filepath
                self.is_modified = False
                self.update_title()
                self.add_to_recent(filepath)
                self.update_status(f"已另存为: {os.path.basename(filepath)}")
                self.add_file_to_tree(filepath)
                return True
            except Exception as e:
                messagebox.showerror("错误", f"保存失败:\n{str(e)}")
                return False
        return False

    def check_save(self):
        if self.is_modified:
            result = messagebox.askyesnocancel("保存更改", "文件已修改，是否保存更改？")
            if result is None: return False
            elif result: return self.save_file()
            else: return True
        return True

    def exit_app(self):
        if self.check_save():
            if self.find_dialog and self.find_dialog.winfo_exists():
                self.find_dialog.destroy()
            self.root.quit()

    def add_to_recent(self, filepath):
        if filepath in self.recent_files:
            self.recent_files.remove(filepath)
        self.recent_files.insert(0, filepath)
        self.recent_files = self.recent_files[:self.max_recent]

    def open_project(self):
        path = filedialog.askdirectory(title="选择项目文件夹")
        if path:
            self.project_dir = path
            self.tree_data = {}
            self.tree.delete(*self.tree.get_children())
            self.load_directory_tree(path, "")

    def select_tree_item_by_path(self, filepath):
        for item_id, data in self.tree_data.items():
            if data.get("path") == filepath:
                self.tree.selection_set(item_id)
                self.tree.see(item_id)
                break

    def add_file_to_tree(self, filepath):
        dirpath = os.path.dirname(filepath)
        parent_id = ""
        for item_id, data in self.tree_data.items():
            if data.get("path") == dirpath and data["type"] == "folder":
                parent_id = item_id
                break
        item_id = self.tree.insert(parent_id, "end", text=os.path.basename(filepath))
        self.tree_data[item_id] = {"path": filepath, "type": "file", "parent": parent_id}

    def show_tree_menu(self, event):
        item = self.tree.identify_row(event.y)
        if item:
            self.tree.selection_set(item)
            self.tree_menu.post(event.x_root, event.y_root)

    def on_tree_double_click(self, event):
        item = self.tree.selection()
        if not item: return
        data = self.tree_data.get(item[0])
        if data and data["type"] == "file":
            self.open_file(data["path"])

    def create_new_file(self):
        parent = self.tree.focus()
        parent_data = self.tree_data.get(parent, {"path": self.project_dir})
        parent_path = parent_data["path"] if parent_data["type"] == "folder" else os.path.dirname(parent_data["path"])
        name = tk.simpledialog.askstring("新建文件", "输入文件名:", initialvalue="新文件.txt")
        if name:
            path = os.path.join(parent_path, name)
            with open(path, 'w', encoding='utf-8') as f:
                f.write("")
            item_id = self.tree.insert(parent, "end", text=name)
            self.tree_data[item_id] = {"path": path, "type": "file", "parent": parent}
            self.update_status(f"已创建: {name}")

    def create_new_folder(self):
        parent = self.tree.focus()
        parent_data = self.tree_data.get(parent, {"path": self.project_dir})
        parent_path = parent_data["path"] if parent_data["type"] == "folder" else os.path.dirname(parent_data["path"])
        name = tk.simpledialog.askstring("新建文件夹", "输入名称:")
        if name:
            path = os.path.join(parent_path, name)
            os.makedirs(path, exist_ok=True)
            item_id = self.tree.insert(parent, "end", text=name, open=True)
            self.tree_data[item_id] = {"path": path, "type": "folder", "parent": parent}
            self.update_status(f"已创建: {name}")

    def rename_item(self):
        item = self.tree.focus()
        if not item: return
        old_text = self.tree.item(item, "text")
        new_text = tk.simpledialog.askstring("重命名", "新名称:", initialvalue=old_text)
        if new_text and new_text != old_text:
            data = self.tree_data[item]
            old_path = data["path"]
            new_path = os.path.join(os.path.dirname(old_path), new_text)
            try:
                os.rename(old_path, new_path)
                self.tree.item(item, text=new_text)
                data["path"] = new_path
                if data["type"] == "file" and self.current_file == old_path:
                    self.current_file = new_path
                    self.update_title()
                self.update_status(f"已重命名: {old_text} → {new_text}")
            except Exception as e:
                messagebox.showerror("错误", str(e))

    def delete_item(self, to_trash=False):
        item = self.tree.focus()
        if not item: return
        data = self.tree_data[item]
        
        if to_trash:
            msg = f"移动到回收站 {data['path']}？"
        else:
            msg = f"删除 {data['path']}？"
            
        if messagebox.askyesno("确认删除", msg, icon='warning'):
            try:
                if to_trash:
                    # 移动到回收站
                    trash_path = os.path.join(self.trash_dir, os.path.basename(data["path"]))
                    # 避免文件名冲突
                    counter = 1
                    while os.path.exists(trash_path):
                        name, ext = os.path.splitext(os.path.basename(data["path"]))
                        trash_path = os.path.join(self.trash_dir, f"{name}_{counter}{ext}")
                        counter += 1
                    shutil.move(data["path"], trash_path)
                else:
                    if data["type"] == "folder":
                        shutil.rmtree(data["path"])
                    else:
                        os.remove(data["path"])
                
                del self.tree_data[item]
                self.tree.delete(item)
                self.update_status("已删除" + ("到回收站" if to_trash else ""))
            except Exception as e:
                messagebox.showerror("错误", str(e))

    def save_project_state(self):
        if not self.project_dir:
            messagebox.showwarning("提示", "请先打开项目")
            return
        config_path = os.path.join(self.project_dir, ".notepad.json")
        state = {"files": [d["path"] for d in self.tree_data.values() if d["type"] == "file"]}
        try:
            with open(config_path, 'w', encoding='utf-8') as f:
                json.dump(state, f, indent=2, ensure_ascii=False)
            messagebox.showinfo("成功", "项目状态已保存")
        except Exception as e:
            messagebox.showerror("错误", f"保存失败: {str(e)}")

    def toggle_bold(self):
        try:
            start = self.text_area.index(tk.SEL_FIRST)
        except tk.TclError:
            messagebox.showinfo("提示", "请先选择要设置格式的文本")
            return

        if "my_bold" in self.text_area.tag_names(start):
            self.text_area.tag_remove("my_bold", tk.SEL_FIRST, tk.SEL_LAST)
            self.bold_var.set(False)
            self.update_status("已取消粗体")
        else:
            current_font = font.Font(font=self.text_area["font"])
            bold_font = (current_font.actual()['family'], current_font.actual()['size'], "bold")
            self.text_area.tag_configure("my_bold", font=bold_font)
            self.text_area.tag_add("my_bold", tk.SEL_FIRST, tk.SEL_LAST)
            self.bold_var.set(True)
            self.update_status("已设置粗体")

    def toggle_italic(self):
        try:
            start = self.text_area.index(tk.SEL_FIRST)
        except tk.TclError:
            messagebox.showinfo("提示", "请先选择要设置格式的文本")
            return

        if "my_italic" in self.text_area.tag_names(start):
            self.text_area.tag_remove("my_italic", tk.SEL_FIRST, tk.SEL_LAST)
            self.italic_var.set(False)
            self.update_status("已取消斜体")
        else:
            current_font = font.Font(font=self.text_area["font"])
            italic_font = (current_font.actual()['family'], current_font.actual()['size'], "italic")
            self.text_area.tag_configure("my_italic", font=italic_font)
            self.text_area.tag_add("my_italic", tk.SEL_FIRST, tk.SEL_LAST)
            self.italic_var.set(True)
            self.update_status("已设置斜体")

    def toggle_underline(self):
        try:
            start = self.text_area.index(tk.SEL_FIRST)
        except tk.TclError:
            messagebox.showinfo("提示", "请先选择要设置格式的文本")
            return

        if "my_underline" in self.text_area.tag_names(start):
            self.text_area.tag_remove("my_underline", tk.SEL_FIRST, tk.SEL_LAST)
            self.underline_var.set(False)
            self.update_status("已取消下划线")
        else:
            self.text_area.tag_configure("my_underline", underline=True)
            self.text_area.tag_add("my_underline", tk.SEL_FIRST, tk.SEL_LAST)
            self.underline_var.set(True)
            self.update_status("已设置下划线")

    def toggle_strikethrough(self):
        try:
            start = self.text_area.index(tk.SEL_FIRST)
        except tk.TclError:
            messagebox.showinfo("提示", "请先选择要设置格式的文本")
            return

        if "my_overstrike" in self.text_area.tag_names(start):
            self.text_area.tag_remove("my_overstrike", tk.SEL_FIRST, tk.SEL_LAST)
            self.update_status("已取消删除线")
        else:
            self.text_area.tag_configure("my_overstrike", overstrike=True)
            self.text_area.tag_add("my_overstrike", tk.SEL_FIRST, tk.SEL_LAST)
            self.update_status("已设置删除线")

    def choose_text_color(self):
        color = colorchooser.askcolor(title="选择文字颜色")[1]
        if color:
            try:
                start = self.text_area.index(tk.SEL_FIRST)
                end = self.text_area.index(tk.SEL_LAST)
                tag_name = f"color_{color.replace('#', '_')}"
                self.text_area.tag_configure(tag_name, foreground=color)
                self.text_area.tag_add(tag_name, start, end)
                self.update_status("已更改文字颜色")
            except tk.TclError:
                messagebox.showinfo("提示", "请先选择要更改颜色的文本")

    def change_font_size(self, size):
        try:
            start = self.text_area.index(tk.SEL_FIRST)
            end = self.text_area.index(tk.SEL_LAST)
            current_font = font.Font(font=self.text_area["font"])
            tag_name = f"size_{size}"
            self.text_area.tag_configure(tag_name, font=(current_font.actual()['family'], size))
            self.text_area.tag_add(tag_name, start, end)
            self.update_status(f"字号已改为 {size}")
        except tk.TclError:
            messagebox.showinfo("提示", "请先选择要设置格式的文本")

    def change_font(self, font_name):
        try:
            start = self.text_area.index(tk.SEL_FIRST)
            end = self.text_area.index(tk.SEL_LAST)
            current_font = font.Font(font=self.text_area["font"])
            tag_name = f"font_{font_name}"
            self.text_area.tag_configure(tag_name, font=(font_name, current_font.actual()['size']))
            self.text_area.tag_add(tag_name, start, end)
            self.update_status(f"字体已改为 {font_name}")
        except tk.TclError:
            messagebox.showinfo("提示", "请先选择要设置格式的文本")

    def update_format_toolbar(self, event=None):
        """更新工具栏按钮状态"""
        try:
            index = self.text_area.index(tk.INSERT)
            tags = self.text_area.tag_names(index)
            self.bold_var.set("my_bold" in tags)
            self.italic_var.set("my_italic" in tags)
            self.underline_var.set("my_underline" in tags)
        except:
            pass

    # ========== 查找替换 ==========
    def show_find_replace(self):
        if hasattr(self, 'find_dialog') and self.find_dialog and self.find_dialog.winfo_exists():
            self.find_dialog.lift()
            return
        self.create_find_replace_dialog()

    def create_find_replace_dialog(self):
        self.find_dialog = tk.Toplevel(self.root)
        self.find_dialog.title("查找和替换")
        self.find_dialog.geometry("400x200")
        self.find_dialog.transient(self.root)
        self.find_dialog.resizable(False, False)

        self.find_text = tk.StringVar()
        self.replace_text = tk.StringVar()
        self.case_var = tk.BooleanVar()

        frame = ttk.Frame(self.find_dialog, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)

        ttk.Label(frame, text="查找内容:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.find_entry = ttk.Entry(frame, width=30, textvariable=self.find_text)
        self.find_entry.grid(row=0, column=1, pady=5)
        self.find_entry.focus()

        ttk.Label(frame, text="替换为:").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.replace_entry = ttk.Entry(frame, width=30, textvariable=self.replace_text)
        self.replace_entry.grid(row=1, column=1, pady=5)

        ttk.Checkbutton(frame, text="匹配大小写", variable=self.case_var).grid(row=2, column=0, sticky=tk.W)

        button_frame = ttk.Frame(frame)
        button_frame.grid(row=3, column=0, columnspan=2, pady=10)

        ttk.Button(button_frame, text="查找下一个", command=self.find_next).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="替换", command=self.replace_current).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="全部替换", command=self.replace_all).pack(side=tk.LEFT, padx=5)

        self.find_entry.bind("<Return>", lambda e: self.find_next())
        self.replace_entry.bind("<Return>", lambda e: self.find_next())

        def on_close():
            self.find_dialog.destroy()
            self.find_dialog = None
        self.find_dialog.protocol("WM_DELETE_WINDOW", on_close)

    def find_next(self):
        search = self.find_text.get()
        if not search: return
        start = self.text_area.index(tk.INSERT) if self.text_area.tag_ranges(tk.SEL) else self.text_area.index("1.0")
        opts = {} if self.case_var.get() else {"nocase": True}
        pos = self.text_area.search(search, start, stopindex=tk.END, **opts)
        if not pos:
            pos = self.text_area.search(search, "1.0", stopindex=start, **opts)
        if pos:
            end = f"{pos}+{len(search)}c"
            self.text_area.tag_remove(tk.SEL, "1.0", tk.END)
            self.text_area.tag_add(tk.SEL, pos, end)
            self.text_area.mark_set(tk.INSERT, end)
            self.text_area.see(pos)
            self.update_status(f"找到: {search}")
        else:
            self.update_status(f"未找到: {search}")

    def replace_current(self):
        search = self.find_text.get()
        replace = self.replace_text.get()
        if not search: return
        try:
            selected = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
            match = selected == search if self.case_var.get() else selected.lower() == search.lower()
            if match:
                self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST)
                self.text_area.insert(tk.SEL_FIRST, replace)
                self.update_status("已替换")
            self.find_next()
        except tk.TclError:
            self.find_next()

    def replace_all(self):
        search = self.find_text.get()
        replace = self.replace_text.get()
        if not search: return
        content = self.text_area.get("1.0", tk.END + "-1c")
        flags = 0 if self.case_var.get() else re.IGNORECASE
        try:
            compiled = re.compile(re.escape(search), flags)
            new_content, count = compiled.subn(replace, content)
            if count > 0:
                self.text_area.delete("1.0", tk.END)
                self.text_area.insert("1.0", new_content)
                self.update_status(f"已替换 {count} 处")
            else:
                self.update_status(f"未找到: {search}")
        except Exception as e:
            messagebox.showerror("错误", str(e))

    # ========== 编辑操作 ==========
    def undo(self):
        try:
            self.text_area.edit_undo()
            self.update_status("已撤销")
        except tk.TclError:
            self.update_status("没有可撤销的操作")

    def redo(self):
        try:
            self.text_area.edit_redo()
            self.update_status("已重做")
        except tk.TclError:
            self.update_status("没有可重做的操作")

    def cut(self):
        try:
            selected = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
            self.clipboard_history.append(selected)
            self.update_clipboard_menu()
            self.text_area.event_generate("<<Cut>>")
            self.update_status("已剪切")
        except tk.TclError:
            self.update_status("未选择任何内容")

    def copy(self):
        try:
            selected = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
            self.clipboard_history.append(selected)
            self.update_clipboard_menu()
            self.text_area.event_generate("<<Copy>>")
            self.update_status("已复制")
        except tk.TclError:
            self.update_status("未选择任何内容")

    def paste(self):
        self.text_area.event_generate("<<Paste>>")
        self.update_status("已粘贴")

    def select_all(self):
        self.text_area.tag_add(tk.SEL, "1.0", tk.END)
        self.text_area.mark_set(tk.INSERT, tk.END)
        self.text_area.see(tk.INSERT)
        self.update_status("已全选")

    def update_clipboard_menu(self):
        pass  # 暂不实现剪贴板历史菜单


if __name__ == "__main__":
    root = tk.Tk()
    app = EnhancedNotepad(root)
    root.mainloop()